/**
 * @author Caius
 * @description
 * @version 1.0
 * @since Created in 2025-01-14
 */

package com.itsense.stock.service.impl;

import com.google.common.collect.Lists;
import com.itsense.stock.face.StockCacheFace;
import com.itsense.stock.mapper.*;
import com.itsense.stock.pojo.entity.StockBlockRtInfo;
import com.itsense.stock.pojo.entity.StockMarketIndexInfo;
import com.itsense.stock.pojo.entity.StockOuterMarketIndexInfo;
import com.itsense.stock.pojo.entity.StockRtInfo;
import com.itsense.stock.service.StockTimerTaskService;
import com.itsense.stock.pojo.vo.StockInfoConfig;
import com.itsense.stock.utils.DateTimeUtil;
import com.itsense.stock.utils.IdWorker;
import com.itsense.stock.utils.ParseType;
import com.itsense.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private RestTemplate restTemplate;

    @Qualifier("stock-com.itsense.stock.pojo.vo.StockInfoConfig")
    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    private HttpEntity<Object> httpEntity;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;

    @Autowired
    private StockCacheFace stockCacheFace;

    @Override
    public void getInnerMarketInfo() {
        //阶段1 采集数据
        //1.1组装URL地址
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getInner());
        //发起请求
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        int statusCode = responseEntity.getStatusCodeValue();
        if (statusCode!=200) {
            //当前请求失败
            log.error("当前时间点{},获取信息失败,状态码{}", DateTime.now().toString("yyyy-MM-dd hh:mm:ss"), statusCode);
            return;
        }
        //获取json数据
        String jsData = responseEntity.getBody();
        log.info("当前时间点{},采集原始数据{}", DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),jsData);
        //阶段2 正则解析数据
        String reg = "var hq_str_(.+)=\"(.+)\";";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(jsData);
        List<StockMarketIndexInfo> entities = new ArrayList<>();
        while (matcher.find()) {
            //获取大盘编码
            String marketCode = matcher.group(1);
            String otherInfo = matcher.group(2);
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName=splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint=new BigDecimal(splitArr[1]);
            //前收盘点
            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint=new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint=new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt=Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
            //时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();
            //阶段3 解析数据之后封装entity
            StockMarketIndexInfo entity = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketName(marketName)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .curPoint(curPoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeAmount(tradeAmt)
                    .tradeVolume(tradeVol)
                    .marketCode(marketCode)
                    .curTime(curTime)
                    .build();
            entities.add(entity);
        }
        log.info("解析大盘数据完毕！");
        //阶段4 将数据插入数据库中
        int count = stockMarketIndexInfoMapper.insertBatch(entities);
        if (count>0) {
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
            log.info("当前时间: {} 插入大盘数据{}成功",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),entities);
        }else{
            log.error("当前时间: {} 插入大盘数据{}失败",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),entities);
        }
    }

    /**
     * 定义获取分钟级股票数据
     */
    @Override
    public void getStockRtIndex() {
        List<String> allStockCodes = stockCacheFace.getAllStockCodeWithPrefix();
//        long startTime = System.currentTimeMillis();
        Lists.partition(allStockCodes,15).forEach(codes->{
//            //原始方案
//            String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//            //发起请求
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            int statusCodeValue = responseEntity.getStatusCodeValue();
//            if (statusCodeValue!=200) {
//                //当前请求失败
//                log.error("当前时间点{},获取信息失败,状态码{}", DateTime.now().toString("yyyy-MM-dd hh:mm:ss"), statusCodeValue);
//                return;
//            }
//            //获取原始的js数据
//            String jsData = responseEntity.getBody();
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//            log.info("采集各股数据{}",list);
//            int count=stockRtInfoMapper.insertBatch(list);
//            if (count>0) {
//                log.info("当前时间: {} 插入个股数据{}成功",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
//            }else{
//                log.error("当前时间: {} 插入个股数据{}失败",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
//            }
            //方案1:使用多线程实现并行操作
            //代码问题: 1.每次来任务就创建一个线程,复用性差 2.如果多线程使用不当,造成cpu竞争激烈,导致频繁的上下文切换，导致程序的性能降低
//            new Thread(()->{
//                String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//                //发起请求
//                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//                int statusCodeValue = responseEntity.getStatusCodeValue();
//                if (statusCodeValue!=200) {
//                    //当前请求失败
//                    log.error("当前时间点{},获取信息失败,状态码{}", DateTime.now().toString("yyyy-MM-dd hh:mm:ss"), statusCodeValue);
//                    return;
//                }
//                //获取原始的js数据
//                String jsData = responseEntity.getBody();
//                List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//                log.info("采集各股数据{}",list);
//                int count=stockRtInfoMapper.insertBatch(list);
//                if (count>0) {
//                    log.info("当前时间: {} 插入个股数据{}成功",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
//                }else{
//                    log.error("当前时间: {} 插入个股数据{}失败",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
//                }
//            }).start();
//        });
            //方案2:使用线程池进行优化
            threadPoolTaskExecutor.execute(()->{
                String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
                //发起请求
                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
                int statusCodeValue = responseEntity.getStatusCodeValue();
                if (statusCodeValue!=200) {
                    //当前请求失败
                    log.error("当前时间点{},获取信息失败,状态码{}", DateTime.now().toString("yyyy-MM-dd hh:mm:ss"), statusCodeValue);
                    return;
                }
                //获取原始的js数据
                String jsData = responseEntity.getBody();
                List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                log.info("采集各股数据{}",list);
                int count=stockRtInfoMapper.insertBatch(list);
                if (count>0) {
                    log.info("当前时间: {} 插入个股数据{}成功",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
                }else{
                    log.error("当前时间: {} 插入个股数据{}失败",DateTime.now().toString("yyyy-MM-dd hh:mm:ss"),list);
                }
            });
        });
//        long takeTime =  System.currentTimeMillis() - startTime;
//        log.info("执行采集任务耗费时间{}ms",takeTime);

    }

    /**
     * 获取板块信息
     * http://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php
     */
    @Override
    public void getStockSectorRtIndex() {
        String result = restTemplate.getForObject(stockInfoConfig.getBlockUrl(),String.class);
        List<StockBlockRtInfo> infos = parserStockInfoUtil.parse4StockBlock(result);
        log.info("获取板块的数量{}",infos.size());
        Lists.partition(infos,20).forEach(codes->{
            //分批插入数据
            stockBlockRtInfoMapper.insertBatch(codes);
        });
    }

    @Override
    public void getOuterMarketInfo() {
        //构建请求头
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getOuter());
        //发起请求
        ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);

        int statusCodeValue = result.getStatusCodeValue();

        if (statusCodeValue != 200) {
            log.error("当前时间点{},数据采集失败,状态码{}",DateTime.now().toString("YYYY-MM-dd HH:mm:ss"),statusCodeValue);
            return;
        }

        String jsData = result.getBody();

        List<StockOuterMarketIndexInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.OUTER);

        log.info("当前获取大盘的信息: {}",list);

        int num = stockOuterMarketIndexInfoMapper.insertBatch(list);

        if(num > 0){
            log.info("当前时间{} , 插入数据成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
        }else{
            log.info("当前时间{}, 插入数据失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
        }
    }
    @PostConstruct
    public void initData(){
        HttpHeaders headers = new HttpHeaders();
        //防盗链
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        //客户端标识
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36");
        //维护http请求实体对象
        httpEntity = new HttpEntity<>(headers);
    }
}
